#include <time.h>

#include "list.h"
#include "volume_proto.h"

#include "fnotify.h"
#include "lba_lock.h"
#include "lsv.h"
#include "lsv_bitmap.h"
#include "lsv_help.h"
#include "lsv_volume.h"

int volume_proto_read_header(lsv_volume_proto_t *lsv_info, const fileid_t *parent, const fileid_t *fileid, const char *snap, void *header);

static void __init_row2_info(lsv_volume_proto_t *lsv_info)
{
        DINFO("start mode %d\n", lsv_info->u.start_mode);

        memset(&lsv_info->share, 0, sizeof(lsv_info->share));
        memset(&lsv_info->row2_stat, 0, sizeof(lsv_info->row2_stat));
        memset(&lsv_info->op_stat, 0, sizeof(op_stat_t) * OP_MAX);
}

int row2_info_format(lsv_volume_proto_t *lsv_info)
{
        int ret;
        YASSERT(lsv_info->u.start_mode == LSV_SYS_CREATE);

        __init_row2_info(lsv_info);

        uint64_t parent_id = lsv_info->volume_proto->table1.fileinfo.source;
        const char *snap = lsv_info->volume_proto->table1.fileinfo.snap;

        ret = lsv_volume_init(lsv_info, LSV_SYS_CREATE);
        if (unlikely(ret)) {
                DINFO("lsv_init:create volume ret:%d\n", ret);
                return ret;
        }

        // chunk_id: 65
        if (!parent_id)
                ret = lsv_bitmap_init(lsv_info, LSV_SYS_CREATE, LSV_BITMAP_FLAG_DISABLE_GC);
        else {
                fileid_t parent, fileid;
                mk_volid(&parent, parent_id);
                mk_volid(&fileid, parent_id);
                struct lsv_bitmap_header *header = malloc(sizeof(struct lsv_bitmap_header) + BITMAP_CHUNK_HEADER_SIZE(lsv_info->size));

                ret = volume_proto_read_header(lsv_info, &parent, &fileid, snap, header);
                if (unlikely(ret)) {
                        DINFO("volume_proto_read_header ret:%d\n", ret);
                        free(header);
                        return ret;
                }

                ret = lsv_bitmap_init_by_buf(lsv_info, parent_id, header, LSV_BITMAP_FLAG_DISABLE_GC);
                if (unlikely(ret)) {
                        DINFO("lsv_bitmap_init_by_buf ret:%d\n", ret);
                        free(header);
                        return ret;
                }
                free(header);
        }

        if (unlikely(ret)) {
                DINFO("init bitmap ret:%d\n", ret);
                return ret;
        }

        ltable_init(&lsv_info->lock_table, "row2_io");
        ltable_init(&lsv_info->bitmap_cow_lt, "row2_cow");
        lsv_rwlock_init(&lsv_info->io_lock);

        lsv_info->row3_tail.chunk_id = 0;
        lsv_info->row3_tail.chunk_off = 0;

        lsv_rwlock_init(&lsv_info->info_lock);

        lsv_bitmap_set_cow_callback(lsv_info, row2_data_cow_callback);

        DWARN("ino %ju\n", lsv_info->ino);
        if (gloconf.volume_crc) {
                check_bitmap_alloc(&lsv_info->crc_context, (uint64_t)10 * 1024 * 1024 * 1024);
        }

        return 0;
}

int row2_info_init(lsv_volume_proto_t *lsv_info)
{
        int ret;
        YASSERT(lsv_info->u.start_mode == LSV_SYS_LOAD || lsv_info->u.start_mode == LSV_SYS_RECOVERY);

        //lsv_info->u.start_mode = LSV_SYS_LOAD;
        //lsv_info->u.start_mode &= ~LSV_SYS_RECOVERY;

        __init_row2_info(lsv_info);

        LSV_TEST_TIME_BEGIN(lsv_init_volume);
        DINFO("lsv_volume_init: %p\n", lsv_info);
        ret = lsv_volume_init(lsv_info, lsv_info->u.start_mode);
        if (unlikely(ret)) {
                DINFO("lsv_init:create volume ret:%d\n", ret);
                return ret;
        }
        LSV_TEST_TIME_END(lsv_init_volume);

        LSV_TEST_TIME_BEGIN(lsv_init_bitmap);
        DINFO("lsv_bitmap_init: %p, mode=%d\n", lsv_info, lsv_info->u.start_mode);
        ret = lsv_bitmap_init(lsv_info, lsv_info->u.start_mode, LSV_BITMAP_FLAG_DISABLE_GC);
        if (unlikely(ret)) {
                DINFO("lsv_init:init bitmap ret:%d\n", ret);
                return ret;
        }
        LSV_TEST_TIME_END(lsv_init_bitmap);

        ltable_init(&lsv_info->lock_table, "row2_io");
        ltable_init(&lsv_info->bitmap_cow_lt, "row2_cow");
        lsv_rwlock_init(&lsv_info->io_lock);

        lsv_info->row3_tail.chunk_id = 0;
        lsv_info->row3_tail.chunk_off = 0;

        lsv_rwlock_init(&lsv_info->info_lock);

        //lsv_bitmap_set_cow_callback(lsv_info, row2_data_cow_callback);

        DWARN("ino %ju\n", lsv_info->ino);
        if (gloconf.volume_crc) {
                check_bitmap_alloc(&lsv_info->crc_context, (uint64_t)10 * 1024 * 1024 * 1024);
        }

        return 0;
}

/**
 * @param lsv_info
 * @param is_flush 删除卷的时候，不能调用flush
 * @return
 */
int row2_info_destroy(lsv_volume_proto_t *lsv_info, int is_flush)
{
        int ret;

        DWARN("ino %ju format %d flush %d\n", lsv_info->ino, lsv_info->volume_format, is_flush);

        if (is_flush) {
                ret = lsv_volume_flush(lsv_info);
                if (unlikely(ret)) {
                        GOTO(err_ret, ret);
                }

                // 设置正常关机标志
                lsv_info_set_poweroff(lsv_info, 1);

                // flush完成
        }

        DWARN("lsv_bitmap_deinit\n");
        ret = lsv_bitmap_deinit(lsv_info);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        DWARN("lsv_volume_delete\n");
        ret = lsv_volume_delete(lsv_info);
        if (unlikely(ret)) {
                GOTO(err_ret, ret);
        }

        ltable_destroy(&lsv_info->lock_table);
        ltable_destroy(&lsv_info->bitmap_cow_lt);

        if (gloconf.volume_crc) {
                check_bitmap_free(lsv_info->crc_context);
        }

        DWARN("finish\n");
        return 0;
err_ret:
        return ret;
}
